package edu.northwestern.cbits.purple_robot_manager.probes.builtin;
import java.util.ArrayList;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.location.GpsSatellite;
import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Looper;
import android.preference.CheckBoxPreference;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import edu.northwestern.cbits.purple_robot_manager.R;
import edu.northwestern.cbits.purple_robot_manager.activities.settings.FlexibleListPreference;
import edu.northwestern.cbits.purple_robot_manager.logging.LogManager;
import edu.northwestern.cbits.purple_robot_manager.probes.Probe;
public class VisibleSatelliteProbe extends Probe implements GpsStatus.Listener, GpsStatus.NmeaListener, LocationListener
{
private static final boolean DEFAULT_ENABLED = false;
private static final String ENABLED = "config_probe_satellites_enabled";
private static final String FREQUENCY = "config_probe_satellites_frequency";
protected Context _context = null;
private final ArrayList<Bundle> _satellites = new ArrayList<>();
private long _lastCheck = 0;
private long _startCheck = 0;
private long _lastTransmit = 0;
private boolean _listening = false;
@Override
public String getPreferenceKey() {
return "built_in_satellites";
}
@Override
public Map<String, Object> configuration(Context context)
{
Map<String, Object> map = super.configuration(context);
SharedPreferences prefs = Probe.getPreferences(context);
long freq = Long.parseLong(prefs.getString(VisibleSatelliteProbe.FREQUENCY, Probe.DEFAULT_FREQUENCY));
map.put(Probe.PROBE_FREQUENCY, freq);
return map;
}
@Override
public String probeCategory(Context context)
{
return context.getString(R.string.probe_external_environment_category);
}
@Override
public void updateFromMap(Context context, Map<String, Object> params)
{
super.updateFromMap(context, params);
if (params.containsKey(Probe.PROBE_FREQUENCY))
{
Object frequency = params.get(Probe.PROBE_FREQUENCY);
if (frequency instanceof Double)
{
frequency = ((Double) frequency).longValue();
}
if (frequency instanceof Long)
{
SharedPreferences prefs = Probe.getPreferences(context);
Editor e = prefs.edit();
e.putString(VisibleSatelliteProbe.FREQUENCY, frequency.toString());
e.commit();
}
}
}
@Override
public String summary(Context context)
{
return context.getString(R.string.summary_visible_satellite_probe_desc);
}
@Override
@SuppressWarnings("deprecation")
public PreferenceScreen preferenceScreen(Context context, PreferenceManager manager)
{
PreferenceScreen screen = super.preferenceScreen(context, manager);
screen.setTitle(this.title(context));
screen.setSummary(R.string.summary_visible_satellite_probe_desc);
CheckBoxPreference enabled = new CheckBoxPreference(context);
enabled.setTitle(R.string.title_enable_probe);
enabled.setKey(VisibleSatelliteProbe.ENABLED);
enabled.setDefaultValue(VisibleSatelliteProbe.DEFAULT_ENABLED);
screen.addPreference(enabled);
FlexibleListPreference duration = new FlexibleListPreference(context);
duration.setKey(VisibleSatelliteProbe.FREQUENCY);
duration.setEntryValues(R.array.probe_satellite_frequency_values);
duration.setEntries(R.array.probe_satellite_frequency_labels);
duration.setTitle(R.string.probe_frequency_label);
duration.setDefaultValue(Probe.DEFAULT_FREQUENCY);
screen.addPreference(duration);
return screen;
}
@Override
public JSONObject fetchSettings(Context context)
{
JSONObject settings = super.fetchSettings(context);
try
{
JSONObject frequency = new JSONObject();
frequency.put(Probe.PROBE_TYPE, Probe.PROBE_TYPE_LONG);
JSONArray values = new JSONArray();
String[] options = context.getResources().getStringArray(R.array.probe_satellite_frequency_values);
for (String option : options)
{
values.put(Long.parseLong(option));
}
frequency.put(Probe.PROBE_VALUES, values);
settings.put(Probe.PROBE_FREQUENCY, frequency);
}
catch (JSONException e)
{
LogManager.getInstance(context).logException(e);
}
return settings;
}
@Override
public void enable(Context context)
{
SharedPreferences prefs = Probe.getPreferences(context);
Editor e = prefs.edit();
e.putBoolean(VisibleSatelliteProbe.ENABLED, true);
e.commit();
}
@Override
public void disable(Context context)
{
SharedPreferences prefs = Probe.getPreferences(context);
Editor e = prefs.edit();
e.putBoolean(VisibleSatelliteProbe.ENABLED, false);
e.commit();
}
@Override
public boolean isEnabled(Context context)
{
LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
if (super.isEnabled(context))
{
this._context = context.getApplicationContext();
long now = System.currentTimeMillis();
SharedPreferences prefs = Probe.getPreferences(context);
if (prefs.getBoolean(VisibleSatelliteProbe.ENABLED, VisibleSatelliteProbe.DEFAULT_ENABLED))
{
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))
{
Looper looper = Looper.myLooper();
if (looper == null)
Looper.prepare();
locationManager.addGpsStatusListener(this);
locationManager.addNmeaListener(this);
synchronized (this)
{
long freq = Long.parseLong(prefs.getString(VisibleSatelliteProbe.FREQUENCY, Probe.DEFAULT_FREQUENCY));
if (now - this._lastCheck > 60000 && now - this._lastCheck < freq) // Try
// to
// get
// satellites
// in
// 60
// seconds...
{
locationManager.removeGpsStatusListener(this);
locationManager.removeNmeaListener(this);
locationManager.removeUpdates(this);
}
if (now - this._lastCheck > freq)
{
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this);
this._lastCheck = now;
this._startCheck = now;
this._listening = true;
}
}
}
return true;
}
}
locationManager.removeGpsStatusListener(this);
locationManager.removeNmeaListener(this);
locationManager.removeUpdates(this);
return false;
}
@Override
public String title(Context context)
{
return context.getString(R.string.title_visible_satellite_probe);
}
@Override
@SuppressWarnings("unchecked")
public void onGpsStatusChanged(int event)
{
long now = System.currentTimeMillis();
if (now - this._lastTransmit < 10000) // 10s
return;
this._lastTransmit = now;
switch (event)
{
case GpsStatus.GPS_EVENT_STARTED:
break;
case GpsStatus.GPS_EVENT_STOPPED:
/*
* this._satellites.clear();
*
* Bundle b = new Bundle();
*
* b.putString("PROBE", this.name(this._context));
* b.putLong("TIMESTAMP", now / 1000);
*
* b.putParcelableArrayList("SATELLITES", (ArrayList<Bundle>)
* this._satellites.clone()); b.putInt("SATELLITE_COUNT",
* this._satellites.size());
*
* synchronized(this) { this.transmitData(b); }
*/
break;
case GpsStatus.GPS_EVENT_FIRST_FIX:
break;
case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
LocationManager locationManager = (LocationManager) this._context.getSystemService(Context.LOCATION_SERVICE);
GpsStatus status = locationManager.getGpsStatus(null);
Iterable<GpsSatellite> sats = status.getSatellites();
this._satellites.clear();
float snrSum = 0;
int snrCount = 0;
for (GpsSatellite sat : sats)
{
Bundle satBundle = new Bundle();
satBundle.putFloat("AZIMUTH", sat.getAzimuth());
satBundle.putFloat("ELEVATION", sat.getElevation());
satBundle.putInt("RANDOM_NUMBER", sat.getPrn());
satBundle.putFloat("SIGNAL_RATIO", sat.getSnr());
satBundle.putBoolean("HAS_ALMANAC", sat.hasAlmanac());
satBundle.putBoolean("HAS_EPHEMERIS", sat.hasEphemeris());
satBundle.putBoolean("USED_IN_FIX", sat.usedInFix());
snrSum += sat.getSnr();
snrCount += 1;
this._satellites.add(satBundle);
}
Bundle bundle = new Bundle();
bundle.putString("PROBE", this.name(this._context));
bundle.putLong("TIMESTAMP", now / 1000);
bundle.putParcelableArrayList("SATELLITES", (ArrayList<Bundle>) this._satellites.clone());
bundle.putInt("SATELLITE_COUNT", this._satellites.size());
if (snrSum > 0)
snrSum = snrSum / snrCount;
bundle.putFloat("AVERAGE_SIGNAL_RATIO", snrSum);
synchronized (this)
{
this.transmitData(this._context, bundle);
}
break;
}
if (this._context != null)
{
LocationManager locationManager = (LocationManager) this._context.getSystemService(Context.LOCATION_SERVICE);
if (now - this._startCheck > 10000 && this._listening)
{
synchronized (this)
{
this._listening = false;
locationManager.removeUpdates(this);
this._startCheck = now;
}
this.isEnabled(this._context);
}
locationManager = null;
}
}
@Override
public String name(Context context)
{
return "edu.northwestern.cbits.purple_robot_manager.probes.builtin.VisibleSatelliteProbe";
}
@Override
public void onNmeaReceived(long timestamp, String nmea)
{
// Log.e("PRM", "NMEA (" + timestamp + "): " + nmea);
}
@Override
public void onLocationChanged(Location location)
{
// Log.e("PRM", "LOCATION CHANGED: " + location);
}
@Override
public String summarizeValue(Context context, Bundle bundle)
{
int count = (int) bundle.getDouble("SATELLITE_COUNT");
double snr = bundle.getDouble("AVERAGE_SIGNAL_RATIO");
return String.format(context.getResources().getString(R.string.summary_satellite_probe), count, snr);
}
@Override
public void onProviderDisabled(String provider)
{
}
@Override
public void onProviderEnabled(String provider)
{
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras)
{
}
private Bundle bundleForSatelliteArray(Context context, ArrayList<Bundle> objects)
{
Bundle bundle = new Bundle();
for (Bundle value : objects)
{
ArrayList<String> keys = new ArrayList<>();
String key = String.format(context.getString(R.string.display_satellite_label), value.getInt("RANDOM_NUMBER"));
Bundle satBundle = new Bundle();
satBundle.putInt(context.getString(R.string.display_satellite_id_label), value.getInt("RANDOM_NUMBER"));
satBundle.putFloat(context.getString(R.string.display_satellite_elevation_label), value.getFloat("ELEVATION"));
satBundle.putFloat(context.getString(R.string.display_satellite_azimuth_label), value.getFloat("AZIMUTH"));
satBundle.putFloat(context.getString(R.string.display_satellite_signal_label), value.getFloat("SIGNAL_RATIO"));
if (value.getBoolean("HAS_EPHEMERIS"))
satBundle.putString(context.getString(R.string.display_satellite_ephemeris_label), context.getString(R.string.display_satellite_has_ephemeris));
else
satBundle.putString(context.getString(R.string.display_satellite_ephemeris_label), context.getString(R.string.display_satellite_no_ephemeris));
if (value.getBoolean("HAS_ALMANAC"))
satBundle.putString(context.getString(R.string.display_satellite_almanac_label), context.getString(R.string.display_satellite_has_almanac));
else
satBundle.putString(context.getString(R.string.display_satellite_almanac_label), context.getString(R.string.display_satellite_no_almanac));
keys.add(context.getString(R.string.display_satellite_id_label));
keys.add(context.getString(R.string.display_satellite_elevation_label));
keys.add(context.getString(R.string.display_satellite_azimuth_label));
keys.add(context.getString(R.string.display_satellite_signal_label));
keys.add(context.getString(R.string.display_satellite_ephemeris_label));
keys.add(context.getString(R.string.display_satellite_almanac_label));
satBundle.putStringArrayList("KEY_ORDER", keys);
bundle.putBundle(key, satBundle);
}
return bundle;
}
@Override
public Bundle formattedBundle(Context context, Bundle bundle)
{
Bundle formatted = super.formattedBundle(context, bundle);
@SuppressWarnings("unchecked")
ArrayList<Bundle> array = (ArrayList<Bundle>) bundle.get("SATELLITES");
int count = (int) bundle.getDouble("SATELLITE_COUNT");
Bundle satsBundle = this.bundleForSatelliteArray(context, array);
formatted.putBundle(String.format(context.getString(R.string.display_satellites_title), count), satsBundle);
formatted.putFloat(context.getString(R.string.display_satellites_signal_ratio), bundle.getFloat("AVERAGE_SIGNAL_RATIO"));
return formatted;
}
public String assetPath(Context context)
{
return "visible-satellites-probe.html";
}
}